/*******************************************************************************
 * Copyright (c) 2009 Dop Sun.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Dop Sun - initial API and implementation
 ******************************************************************************/
package com.softao.ming.gde.internal.ui.launcher;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.jdt.launching.AbstractJavaLaunchConfigurationDelegate;
import org.eclipse.jdt.launching.ExecutionArguments;
import org.eclipse.jdt.launching.IVMRunner;
import org.eclipse.jdt.launching.VMRunnerConfiguration;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;

import com.softao.ming.gde.core.BundleRegistry;
import com.softao.ming.gde.core.launcher.LauncherContext;
import com.softao.ming.gde.core.launcher.LauncherSettings;
import com.softao.ming.gde.internal.core.launcher.tasks.BootstrapApplicationTask;
import com.softao.ming.gde.internal.core.launcher.tasks.PrepareTask;
import com.softao.ming.gde.internal.core.natures.GdePluginFile;
import com.softao.ming.gde.internal.core.util.LaunchUtil;
import com.softao.ming.gde.internal.ui.preferences.InstalledGwt;
import com.softao.ming.gde.internal.ui.preferences.InstalledGwt.GwtDefinition;

/**
 * 
 */
public class GdeApplicationLaunchConfigurationDelegate 
		extends AbstractJavaLaunchConfigurationDelegate 
		implements ILaunchConfigurationDelegate {

	@SuppressWarnings("unchecked")
	@Override
	public void launch(ILaunchConfiguration configuration, String mode,
			ILaunch launch, IProgressMonitor monitor) throws CoreException {
		if (monitor == null) {
			monitor = new NullProgressMonitor();
		}

		monitor.beginTask(MessageFormat.format(
				"{0}...", new Object[] { configuration.getName() }), 4); //$NON-NLS-1$

		LauncherContext context = buildLaunchContext(configuration);
		PrepareTask prepareTask = new PrepareTask();
		prepareTask.perform(context, monitor);
		
		BootstrapApplicationTask bootstrapTask = new BootstrapApplicationTask();
		bootstrapTask.perform(context, monitor);
		/*
		BootstrapGeneratorArgs args = new BootstrapGeneratorArgs();
		args.setLocation(LaunchUtil.getWorkspaceDataLocation(configuration));
		args.setUseProduct(configuration.getAttribute("useProduct", false));
		if (args.isUseProduct()) {
			args.setTargetId(configuration.getAttribute("product", (String)null));
		} else {
			args.setTargetId(configuration.getAttribute("application", (String)null));
		}
		
		BootstrapGenerator generator = new BootstrapGenerator();
		generator.generate(args, monitor);
		*/
		// check for cancellation
		if (monitor.isCanceled()) {
			return;
		}
		try {
			monitor.subTask("Verifying launch attributes...");

			String mainTypeName = verifyMainTypeName(configuration);
			IVMRunner runner = getVMRunner(configuration, mode);

			File workingDir = verifyWorkingDirectory(configuration);
			String workingDirName = null;
			if (workingDir != null) {
				workingDirName = workingDir.getAbsolutePath();
			}

			// Environment variables
			String[] envp = getEnvironment(configuration);

			// Program & VM arguments
			String pgmArgs = getProgramArguments(configuration);
			String vmArgs = getVMArguments(configuration);
			ExecutionArguments execArgs = new ExecutionArguments(vmArgs,
					pgmArgs);

			// VM-specific attributes
			Map vmAttributesMap = getVMSpecificAttributesMap(configuration);

			// Classpath
			String[] classpath = getClasspath(configuration);

			// Create VM config
			VMRunnerConfiguration runConfig = new VMRunnerConfiguration(
					mainTypeName, classpath);
			runConfig.setProgramArguments(execArgs.getProgramArgumentsArray());
			runConfig.setEnvironment(envp);
			runConfig.setVMArguments(execArgs.getVMArgumentsArray());
			runConfig.setWorkingDirectory(workingDirName);
			runConfig.setVMSpecificAttributesMap(vmAttributesMap);

			// Bootpath
			runConfig.setBootClassPath(getBootpath(configuration));

			// check for cancellation
			if (monitor.isCanceled()) {
				return;
			}

			// stop in main
			prepareStopInMain(configuration);

			// done the verification phase
			monitor.worked(1);

			monitor.subTask("Creating source locator...");
			// set the default source locator if required
			setDefaultSourceLocator(launch, configuration);
			monitor.worked(1);

			// Launch the configuration - 1 unit of work
			runner.run(runConfig, launch, monitor);

			// check for cancellation
			if (monitor.isCanceled()) {
				return;
			}
		} finally {
			monitor.done();
		}
	}

	private LauncherContext buildLaunchContext(ILaunchConfiguration configuration) throws CoreException {
		LauncherSettings settings = new LauncherSettings();
		GwtDefinition gwtDefinition = loadDefaultGwtDefinition();
		if (gwtDefinition == null) {
			throw new RuntimeException("GWT not defined in preference.");
		}
		settings.setPlatformLocations(new String[] {});
		settings.setProjectLocations(new String[] {});
		settings.setTargetElementId(configuration.getAttribute("product", (String)null));
		settings.setConfigurationLocations(new String[] {});
		settings.setGwtInstallationLocation(gwtDefinition.getPath());
		settings.setOutputLocation(LaunchUtil.getWorkspaceDataLocation(configuration));
		
		LauncherContext context = new LauncherContext(settings);
		BundleRegistry registry = context.getBundleRegistry();
		
		for (IPluginModelBase plugin : PluginRegistry.getActiveModels()) {
			if ("com.google.gwt.user".equals(plugin.getBundleDescription().getName())) {
				continue;
			}
			File installedLocation = new File(plugin.getInstallLocation());
			registry.registrySingle(installedLocation);
		}
		
		return context;
	}
	
	@Override
	public String getMainTypeName(ILaunchConfiguration configuration)
			throws CoreException {
		return "com.google.gwt.dev.HostedMode";
	}
	
	@Override
	public String[] getClasspath(ILaunchConfiguration configuration)
			throws CoreException {
		List<String> result = new ArrayList<String>();
		for (IPluginModelBase plugin : PluginRegistry.getActiveModels()) {
			if ("com.google.gwt.user".equals(plugin.getBundleDescription().getName())) {
				continue;
			}
			
			String installLocation = plugin.getInstallLocation();
			File file = new File(installLocation);
			
			if (file.isFile()) {
				try {
					ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ);
					if (zipFile.getEntry(GdePluginFile.FILE_NAME) != null) {
						result.add(file.getPath());
					}
					zipFile.close();
				} catch (IOException e) {
					e.printStackTrace();
					continue;
				}
				
			} else {
				File gwtPluginFile = new File(file, GdePluginFile.FILE_NAME);
				if (!gwtPluginFile.exists()) {
					continue;
				}
				result.add(file.getPath());
				
				File buildFile = new File(file, "build.properties");
				if (!buildFile.exists()) {
					continue;
				}
				
				String[] sourceFolders;
				try {
					InputStream buildEntryStream = new FileInputStream(buildFile);
					sourceFolders = LaunchUtil.getSourceFolders(buildEntryStream);
					buildEntryStream.close();
					
					if (sourceFolders.length == 0) {
						continue;
					}
				} catch (IOException e) {
					e.printStackTrace();
					continue;
				}

				for (String sourceFolder : sourceFolders) {
					File sourceFile = new File(file, sourceFolder);
					result.add(sourceFile.getPath());
				}
			}
		}
		
		GwtDefinition gwtDefinition = loadDefaultGwtDefinition();
		if (gwtDefinition == null) {
			throw new RuntimeException("GWT not defined");
		}
		File gwtFile = new File(gwtDefinition.getPath());
		result.add((new File(gwtFile, "gwt-dev-windows.jar")).getPath());
		result.add((new File(gwtFile, "gwt-servlet.jar")).getPath());
		result.add((new File(gwtFile, "gwt-user.jar")).getPath());
		
		String configurationLocation = configuration.getAttribute("configurationLocation", "");
		if (configurationLocation != null && configurationLocation.trim().length() > 0) {
			result.add(configurationLocation);
		}
		result.add(LaunchUtil.getWorkspaceDataLocation(configuration) + "/src");
		return result.toArray(new String[result.size()]);
	}
	
	private GwtDefinition loadDefaultGwtDefinition() {
		List<GwtDefinition> list = InstalledGwt.load();
		for (GwtDefinition gwtDefinition : list) {
			if (gwtDefinition.isDefault()) {
				return gwtDefinition;
			}
		}
		return null;
	}
	
	@Override
	public String getProgramArguments(ILaunchConfiguration configuration)
			throws CoreException {
		return "-startupUrl default.html com.softao.ming.gwt.bootstrap.MingBootstrap";
	}
	
	@Override
	protected File getDefaultWorkingDirectory(ILaunchConfiguration configuration)
			throws CoreException {
		return new File(LaunchUtil.getWorkspaceDataLocation(configuration));
	}

	@Override
	protected void setDefaultSourceLocator(ILaunch launch,
			ILaunchConfiguration configuration) throws CoreException {
		if (launch.getSourceLocator() == null) {
			ISourceLookupDirector sourceLocator = new GwtSourceLookupDirector(launch, configuration);
			sourceLocator
					.setSourcePathComputer(getLaunchManager()
							.getSourcePathComputer(
									"org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer")); //$NON-NLS-1$
			sourceLocator.initializeDefaults(configuration);
			launch.setSourceLocator(sourceLocator);
		}
	}

}
